{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5f7fa282-04ea-44cc-936a-edda6e96b425",
   "metadata": {},
   "source": [
    "与使用 Query、Path 和 Body 在路径操作函数中声明额外的校验和元数据的方式相同，你可以使用 Pydantic 的 Field 在 Pydantic 模型内部声明校验和元数据。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6537ea7d-a9c1-47d5-8e94-d3c180cc7261",
   "metadata": {},
   "source": [
    "首先，你必须导入Field，然后，你可以对模型属性使用 Field：\n",
    "\n",
    "注意，Field 是直接从 pydantic 导入的，而不是像其他的（Query，Path，Body 等）都从 fastapi 导入。\n",
    "\n",
    "Field 的工作方式和 Query、Path 和 Body 相同，包括它们的参数等等也完全相同。\n",
    "\n",
    "你可以在 Field、Query、Path、Body 中声明额外的信息。这些信息将包含在生成的 JSON Schema 中。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "512e6773-1d12-454e-ab51-a3344a081933",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Started server process [5076]\n",
      "INFO:     Waiting for application startup.\n",
      "INFO:     Application startup complete.\n",
      "INFO:     Uvicorn running on http://0.0.0.0:8009 (Press CTRL+C to quit)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     127.0.0.1:57493 - \"POST /items/5 HTTP/1.1\" 200 OK\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Shutting down\n",
      "INFO:     Waiting for application shutdown.\n",
      "INFO:     Application shutdown complete.\n",
      "INFO:     Finished server process [5076]\n"
     ]
    }
   ],
   "source": [
    "import uvicorn\n",
    "from typing import Annotated\n",
    "from fastapi import Body, FastAPI\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "app = FastAPI()\n",
    "\n",
    "\n",
    "class Item(BaseModel):\n",
    "    name: str\n",
    "    description: str | None = Field(\n",
    "        default=None, title=\"The description of the item\", max_length=300\n",
    "    )\n",
    "    price: float = Field(gt=0, description=\"The price must be greater than zero\")\n",
    "    tax: float | None = None\n",
    "\n",
    "\n",
    "@app.post(\"/items/{item_id}\")\n",
    "async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):\n",
    "    results = {\"item_id\": item_id, \"item\": item}\n",
    "    return results\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    config = uvicorn.Config(app, host='0.0.0.0', port=8009)\n",
    "    server = uvicorn.Server(config)\n",
    "    await server.serve()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8313efcf-a66c-4eb5-84e8-a4ac09d950c4",
   "metadata": {},
   "source": [
    "## requests库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2a54efdd-38d0-4811-a115-bda8f0ac44c7",
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "url = 'http://127.0.0.1:8009/items/5' \n",
    "data = {\n",
    "    \"item\":{\"name\": \"细胞生物学\", \"description\": \"考研书籍\", \"price\":35.8, \"tax\":0.6}\n",
    "}\n",
    "res = requests.post(url, json=data) \n",
    "res.text\n",
    "\n",
    "# 在另一个ipynb文件中运行代码，会得到\n",
    "# '{\"item_id\":5,\"item\":{\"name\":\"细胞生物学\",\"description\":\"考研书籍\",\"price\":35.8,\"tax\":0.6}}'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "840de07b-4f69-4add-9d48-6dd54367911e",
   "metadata": {},
   "source": [
    "## 模式的额外信息 - 例子"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83065437-eec4-40c8-975c-f3b2ae333fdd",
   "metadata": {},
   "source": [
    "您可以在JSON模式中定义额外的信息。\n",
    "\n",
    "一个常见的用例是添加一个将在文档中显示的example。\n",
    "\n",
    "有几种方法可以声明额外的 JSON 模\n",
    "\n",
    "您可以使用 Config 和 schema_extra 为Pydantic模型声明一个示例\n",
    "\n",
    "这些额外的信息将按原样添加到输出的JSON模式中。式信息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "94a2e70d-5b1f-465a-b017-d5690de9a270",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Started server process [5076]\n",
      "INFO:     Waiting for application startup.\n",
      "INFO:     Application startup complete.\n",
      "INFO:     Uvicorn running on http://0.0.0.0:8009 (Press CTRL+C to quit)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     127.0.0.1:57633 - \"POST /items/5 HTTP/1.1\" 200 OK\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Shutting down\n",
      "INFO:     Waiting for application shutdown.\n",
      "INFO:     Application shutdown complete.\n",
      "INFO:     Finished server process [5076]\n"
     ]
    }
   ],
   "source": [
    "import uvicorn\n",
    "from typing import Annotated\n",
    "from fastapi import Body, FastAPI\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "app = FastAPI()\n",
    "\n",
    "\n",
    "class Item(BaseModel):\n",
    "    name: str\n",
    "    description: str | None = Field(\n",
    "        default=None, title=\"The description of the item\", max_length=300\n",
    "    )\n",
    "    price: float = Field(gt=0, description=\"The price must be greater than zero\")\n",
    "    tax: float | None = None\n",
    "    model_config = {\n",
    "        \"json_schema_extra\": {\n",
    "            \"examples\": [\n",
    "                {\n",
    "                    \"name\": \"Foo\",\n",
    "                    \"description\": \"A very nice Item\",\n",
    "                    \"price\": 35.4,\n",
    "                    \"tax\": 3.2,\n",
    "                }\n",
    "            ]\n",
    "        }\n",
    "    }\n",
    "\n",
    "\n",
    "@app.post(\"/items/{item_id}\")\n",
    "async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):\n",
    "    results = {\"item_id\": item_id, \"item\": item}\n",
    "    return results\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    config = uvicorn.Config(app, host='0.0.0.0', port=8009)\n",
    "    server = uvicorn.Server(config)\n",
    "    await server.serve()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "92d060c4-3d83-49d8-81fd-dd9cbda88441",
   "metadata": {},
   "source": [
    "## fastapi提供的docs界面"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58e454b0-3992-488e-b19a-0d167e26130d",
   "metadata": {},
   "source": [
    "在根目录输入/docs 即可打开。\n",
    "\n",
    "打开后选中 POST /items/ 这个接口，打开下拉窗口\n",
    "\n",
    "可以看到 Parameters下面 item_id 被标红星，Required\n",
    "\n",
    "在Request body Required下方的大输入框中可以看到例子已经在里面了\n",
    "{\n",
    "  \"item\": {\n",
    "    \"description\": \"A very nice Item\",\n",
    "    \"name\": \"Foo\",\n",
    "    \"price\": 35.4,\n",
    "    \"tax\": 3.2\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f39251b5-8798-474d-a68d-ecff8dc031db",
   "metadata": {},
   "source": [
    "## Field 的附加参数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "440ba547-18ce-43e2-b6db-d0ced33e0531",
   "metadata": {},
   "source": [
    "在 Field, Path, Query, Body 和其他你之后将会看到的工厂函数，你可以为JSON 模式声明额外信息，你也可以通过给工厂函数传递其他的任意参数来给JSON 模式声明额外信息，比如增加 example:\n",
    "\n",
    "请记住，传递的那些example不会添加任何验证，只会添加注释，用于文档的目的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "2e25dc0c-6912-472c-9366-82b4052f622d",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Started server process [5076]\n",
      "INFO:     Waiting for application startup.\n",
      "INFO:     Application startup complete.\n",
      "INFO:     Uvicorn running on http://0.0.0.0:8009 (Press CTRL+C to quit)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     127.0.0.1:57849 - \"POST /items/5 HTTP/1.1\" 200 OK\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Shutting down\n",
      "INFO:     Waiting for application shutdown.\n",
      "INFO:     Application shutdown complete.\n",
      "INFO:     Finished server process [5076]\n"
     ]
    }
   ],
   "source": [
    "import uvicorn\n",
    "from typing import Annotated\n",
    "from fastapi import Body, FastAPI\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "app = FastAPI()\n",
    "\n",
    "class Item(BaseModel):\n",
    "    name: str = Field(examples=[\"Foo\"])\n",
    "    description: str | None = Field(default=None, examples=[\"A very nice Item\"])\n",
    "    price: float = Field(examples=[35.4])\n",
    "    tax: float | None = Field(default=None, examples=[3.2])\n",
    "\n",
    "\n",
    "@app.post(\"/items/{item_id}\")\n",
    "async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):\n",
    "    results = {\"item_id\": item_id, \"item\": item}\n",
    "    return results\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    config = uvicorn.Config(app, host='0.0.0.0', port=8009)\n",
    "    server = uvicorn.Server(config)\n",
    "    await server.serve()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b0e31f6b-51a9-4bed-a32c-604d0e42e834",
   "metadata": {},
   "outputs": [],
   "source": [
    "使用Field声明示例与使用 Config 和 schema_extra 为Pydantic模型声明示例效果相同。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b44918b0-f22a-4861-a973-326dac248dc5",
   "metadata": {},
   "source": [
    "## Body 额外参数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11d2fad1-661d-402a-bc6b-89caf10cab5d",
   "metadata": {},
   "source": [
    "你可以通过传递额外信息给 Field 同样的方式操作Path, Query, Body等。\n",
    "\n",
    "比如，你可以将请求体的一个 example 传递给 Body:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc392dd6-a1b6-45c5-b857-265fb8efc4da",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Started server process [15404]\n",
      "INFO:     Waiting for application startup.\n",
      "INFO:     Application startup complete.\n",
      "INFO:     Uvicorn running on http://0.0.0.0:8009 (Press CTRL+C to quit)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     127.0.0.1:56677 - \"GET /docs HTTP/1.1\" 200 OK\n",
      "INFO:     127.0.0.1:56677 - \"GET /openapi.json HTTP/1.1\" 200 OK\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Shutting down\n",
      "INFO:     Waiting for application shutdown.\n",
      "INFO:     Application shutdown complete.\n",
      "INFO:     Finished server process [15404]\n"
     ]
    }
   ],
   "source": [
    "import uvicorn\n",
    "from typing import Annotated\n",
    "from fastapi import Body, FastAPI\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "app = FastAPI()\n",
    "\n",
    "class Item(BaseModel):\n",
    "    name: str\n",
    "    description: str | None = None\n",
    "    price: float\n",
    "    tax: float | None = None\n",
    "\n",
    "body_examples = {\n",
    "    \"name\": \"细胞生物学\",\n",
    "    \"description\": \"考研书籍\",\n",
    "    \"price\": 35.8,\n",
    "    \"tax\": 0.6,\n",
    "}\n",
    "\n",
    "\n",
    "@app.post(\"/items/{item_id}\")\n",
    "async def update_item(item_id: int, item: Annotated[Item, Body(embed=True,example=body_examples)]):\n",
    "    results = {\"item_id\": item_id, \"item\": item}\n",
    "    return results\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    config = uvicorn.Config(app, host='0.0.0.0', port=8009)\n",
    "    server = uvicorn.Server(config)\n",
    "    await server.serve()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0fbccc18-c74b-4bf2-b530-92a776986850",
   "metadata": {},
   "source": [
    "关于 example 和 examples...\n",
    "如果参数用example，比如Body(embed=True,example=body_examples)\n",
    "那么就只需要传入一个字典，这个字典就是一个例子。\n",
    "\n",
    "如果参数用examples，比如Body(embed=True,examples=body_examples)\n",
    "那么例子必须被放入列表中，可以放多个，但是docs中只会显示第一个。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "159ae481-1c1f-47eb-9459-524f75818f3b",
   "metadata": {},
   "source": [
    "## 额外数据类型"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ab44fa93-2bed-4049-93fc-e7f164b7c809",
   "metadata": {},
   "source": [
    "到目前为止，您一直在使用常见的数据类型，如:\n",
    "\n",
    "int\n",
    "float\n",
    "str\n",
    "\n",
    "但是您也可以使用更复杂的数据类型。\n",
    "\n",
    "下面是一些你可以使用的其他数据类型:\n",
    "\n",
    "UUID:\n",
    "一种标准的 \"通用唯一标识符\" ，在许多数据库和系统中用作ID。\n",
    "在请求和响应中将以 str 表示。\n",
    "datetime.datetime:\n",
    "一个 Python datetime.datetime.\n",
    "在请求和响应中将表示为 ISO 8601 格式的 str ，比如: 2008-09-15T15:53:00+05:00.\n",
    "datetime.date:\n",
    "Python datetime.date.\n",
    "在请求和响应中将表示为 ISO 8601 格式的 str ，比如: 2008-09-15.\n",
    "datetime.time:\n",
    "一个 Python datetime.time.\n",
    "在请求和响应中将表示为 ISO 8601 格式的 str ，比如: 14:23:55.003.\n",
    "datetime.timedelta:\n",
    "一个 Python datetime.timedelta.\n",
    "在请求和响应中将表示为 float 代表总秒数。\n",
    "Pydantic 也允许将其表示为 \"ISO 8601 时间差异编码\"\n",
    "bool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "44bd17e8-a8e8-488c-81f5-efcac464fb23",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Started server process [5076]\n",
      "INFO:     Waiting for application startup.\n",
      "INFO:     Application startup complete.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Uvicorn running on http://0.0.0.0:8009 (Press CTRL+C to quit)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     127.0.0.1:58014 - \"POST /items/b6abc40a-2d46-452f-aff8-fdcf22e3dfa7 HTTP/1.1\" 200 OK\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Shutting down\n",
      "INFO:     Waiting for application shutdown.\n",
      "INFO:     Application shutdown complete.\n",
      "INFO:     Finished server process [5076]\n"
     ]
    }
   ],
   "source": [
    "import uvicorn\n",
    "from datetime import datetime, time, timedelta\n",
    "from typing import Annotated\n",
    "from uuid import UUID\n",
    "from fastapi import Body, FastAPI\n",
    "\n",
    "app = FastAPI()\n",
    "\n",
    "@app.post(\"/items/{item_id}\")\n",
    "async def read_items(\n",
    "    item_id: UUID,\n",
    "    start_datetime: Annotated[datetime | None, Body()] = None,\n",
    "    end_datetime: Annotated[datetime | None, Body()] = None,\n",
    "    repeat_at: Annotated[time | None, Body()] = None,\n",
    "    process_after: Annotated[timedelta | None, Body()] = None,\n",
    "):\n",
    "    start_process = start_datetime + process_after\n",
    "    duration = end_datetime - start_process\n",
    "    return {\n",
    "        \"item_id\": item_id,\n",
    "        \"start_datetime\": start_datetime,\n",
    "        \"end_datetime\": end_datetime,\n",
    "        \"repeat_at\": repeat_at,\n",
    "        \"process_after\": process_after,\n",
    "        \"start_process\": start_process,\n",
    "        \"duration\": duration,\n",
    "    }\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    config = uvicorn.Config(app, host='0.0.0.0', port=8009)\n",
    "    server = uvicorn.Server(config)\n",
    "    await server.serve()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1b2c7b3-418e-4dc7-8be3-913816406309",
   "metadata": {},
   "source": [
    "## requests库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "5a568936-043b-485d-a734-a3d45290819e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b6abc40a-2d46-452f-aff8-fdcf22e3dfa7\n"
     ]
    }
   ],
   "source": [
    "import uuid\n",
    " \n",
    "# 生成一个随机的 UUID\n",
    "random_uuid = uuid.uuid4()\n",
    "print(random_uuid)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e522a99-b9b4-4490-8432-c8ae088c023f",
   "metadata": {},
   "outputs": [],
   "source": [
    "url = 'http://127.0.0.1:8009/items/b6abc40a-2d46-452f-aff8-fdcf22e3dfa7' \n",
    "data = {\n",
    "  \"start_datetime\": \"2024-01-24 07:42:54\",\n",
    "  \"end_datetime\": \"2024-01-24 07:43:54\",\n",
    "  \"repeat_at\": \"07:41:54\",\n",
    "  \"process_after\": 23.567\n",
    "}\n",
    "res = requests.post(url, json=data) \n",
    "res.text\n",
    "\n",
    "# 在另一个ipynb文件中运行代码，会得到\n",
    "# {\n",
    "#   \"item_id\": \"b6abc40a-2d46-452f-aff8-fdcf22e3dfa7\",\n",
    "#   \"start_datetime\": \"2024-01-24T07:41:54\",\n",
    "#   \"end_datetime\": \"2024-01-24T07:42:54\",\n",
    "#   \"repeat_at\": \"07:41:54\",\n",
    "#   \"process_after\": 23.567,\n",
    "#   \"start_process\": \"2024-01-24T07:42:17.567000\",\n",
    "#   \"duration\": 36.433\n",
    "# }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "006f2c25-164c-4a9b-a441-24907b6414a7",
   "metadata": {},
   "source": [
    "fastapi 会帮我们检查是否是符合UUID、datetime、date、time 的格式\n",
    "\n",
    "比如  \"2024-01-24 07:41:54\" 是可以的，\"2024-01-32 07:41:54\" 是不行的，因为1月没有第32天。\n",
    "\n",
    "\"07:41\" 会被当做 \"07:41:00\""
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
